home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 02 - Basic Game Graphics / Shared Demo Code ƒ / Utils.c next >
Encoding:
Text File  |  1995-03-30  |  14.7 KB  |  579 lines  |  [TEXT/MMCC]

  1. //\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
  2. //
  3. //    Utils.c
  4. //
  5. //    Utility functions.
  6. //
  7. //    History:
  8. //
  9. //    950221jb: created abridged set of utility functions for demo projects
  10. //
  11. //\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
  12.  
  13.  
  14. //  __#Defines________________________________________________________________________
  15. #define    kSleepTicks                    0xFFFFFFFF    //relinquish all time; don't want null events
  16.  
  17. #define kGestaltTrapNumber            0xA1AD
  18. #define kInitGrafTrapNumber            0xA86E
  19. #define kUnimplementedTrapNumber    0x9F
  20.  
  21. #define kOurStopAlertID                999            //ID of ALRT resource
  22.  
  23. #define kIndexedGDeviceType            0
  24. #define kFixedGDeviceType            1
  25. #define kDirectGDeviceType            2
  26.  
  27. //  __#Headers________________________________________________________________________
  28. #include "Utils.h"
  29.  
  30. //  __#Protos_________________________________________________________________________
  31. //  __ Macros_________________________________________________________________________
  32. //  __ Enums__________________________________________________________________________
  33. //  __ Typedefs_______________________________________________________________________
  34. //  __ Static Protos__________________________________________________________________
  35. static Boolean TrapAvailable( short theTrap );
  36.  
  37. //  __ Extern Globals_________________________________________________________________
  38. RGBColor        blackRGB = {0, 0, 0};
  39. RGBColor        grayRGB = {32767, 32767, 32767};
  40. RGBColor        whiteRGB = {65535, 65535, 65535};
  41.  
  42. //  __ Static Globals_________________________________________________________________
  43.  
  44. //  __ Functions______________________________________________________________________
  45.  
  46.  
  47.  
  48.  
  49. //____ ToolBoxInit __________________________________________________________________________
  50. //
  51. //    Basic initialization.
  52. //
  53. void ToolBoxInit( void )
  54. {    
  55.     InitGraf(&qd.thePort);
  56.     InitFonts();
  57.     InitWindows();
  58.     InitMenus();
  59.     TEInit();
  60.     InitDialogs(NULL);
  61.     InitCursor();
  62.     FlushEvents(everyEvent, 0);
  63. }//ToolBoxInit
  64.  
  65.  
  66. //____ WaitForQuit __________________________________________________________________________
  67. //
  68. //    Hangs out until there is any keyboard or mouse activity. This
  69. //    is not a very friendly or robust event loop! For demo only!
  70. //
  71. void WaitForQuit( void )
  72. {
  73. Boolean    wait;
  74. EventRecord        theEvent;
  75.     
  76.     wait = TRUE;
  77.     while (wait)
  78.     {
  79.         if(WaitNextEvent(everyEvent, &theEvent, kSleepTicks, 0L))
  80.         {
  81.             switch (theEvent.what)
  82.             {
  83.                 case mouseDown:
  84.                 case keyDown:
  85.                     wait = FALSE;
  86.                 break;
  87.             }//switch (theEvent.what)
  88.         }//if WaitNextEvent
  89.     }//while
  90.  
  91. }//WaitForQuit
  92.  
  93.  
  94. //____ TrapAvailable __________________________________________________________________________
  95. //
  96. //    Determines if a given trap exists on the Macintosh we're running on.
  97. //
  98. static Boolean TrapAvailable( short theTrap )
  99. {
  100. TrapType        theTrapType;        // Trap type
  101. short            numTraps;            // Number of Toolbox traps
  102. Boolean            trapAvail;            // True if the trap is available, false if not.
  103.     
  104.     if ( ( theTrap & 0x0800 ) > 0 )
  105.         theTrapType = ToolTrap;
  106.     else
  107.         theTrapType = OSTrap;
  108.     
  109.     if ( theTrapType == ToolTrap )
  110.     {
  111.         theTrap = theTrap & 0x07FF;
  112.         
  113.         if ( NGetTrapAddress( kInitGrafTrapNumber, ToolTrap ) == NGetTrapAddress( 0xAA6E, ToolTrap ) )
  114.             numTraps = 0x0200;
  115.         else
  116.             numTraps = 0x0400;
  117.         
  118.         if ( theTrap >= numTraps )
  119.             theTrap = kUnimplementedTrapNumber;
  120.     }
  121.     
  122.     trapAvail = NGetTrapAddress( theTrap, theTrapType ) != NGetTrapAddress( kUnimplementedTrapNumber, ToolTrap );
  123.     
  124.     return(trapAvail);
  125.     
  126. }// TrapAvailable
  127.  
  128.  
  129.  
  130.  
  131. //____ EnviroCheck __________________________________________________________________________
  132. //
  133. //    This checks out the current system and tells us if the amenities  we 
  134. //    require are available.
  135. //
  136. //    For our demos, we're insist on System Seven and a Main Device which is
  137. //    color-capable not in black-and-white mode.
  138. //
  139. Boolean EnviroCheck( void )
  140. {
  141. Boolean        returnMe = FALSE;
  142. GDHandle    theMainGDev;
  143. long        theSystemVersion;
  144.  
  145.     //determine if Gestalt is available…
  146.     if (!TrapAvailable( kGestaltTrapNumber ))
  147.     {
  148.         ShowStopAlert( "\pGestalt trap unavailable on this System." );
  149.         goto Xit;
  150.     }
  151.     
  152.     //Make sure we have at least a minimal System level of functionality
  153.     Gestalt( gestaltSystemVersion, &theSystemVersion );
  154.     
  155.     //We are requiring System Seven
  156.     if ( LoWord( theSystemVersion ) < 0x0700 )        // Check for System Seven
  157.     {
  158.         ShowStopAlert( "\pThe System installed is earlier than System Seven." );
  159.         goto Xit;
  160.     }
  161.     
  162. //    Note: Since we're requiring System Seven, we don't have to wonder if
  163. //    32-Bit QuickDraw is installed. However, if you must be backwards
  164. //    compatible, you can do an explicit check for it:
  165. //
  166. //    Gestalt( gestaltQuickdrawVersion, &qdVersion );
  167. //    if ( qdVersion < gestalt32BitQD )    // Some version of 32 Bit Color Quickdraw present...
  168. //    {
  169. //        ShowStopAlert( "\pAaaack! For some reason we need 32-Bit QuickDraw and it's not here!" );
  170. //        goto Xit;
  171. //    }
  172.  
  173.  
  174.     theMainGDev = GetMainDevice();
  175. //    Our demos won't have a problem if the Main GDevice is a Fixed device, but if you want to
  176. //    determine if the main GDevice is a Fixed type (like on PowerBooks), here's how:
  177. //    if (kFixedGDeviceType == (**theMainGDev).gdType)
  178. //    {
  179. //        ShowStopAlert( "\pMain GDevice is a Fixed Device." );
  180. //        goto Xit;
  181. //    }
  182.  
  183.     // Is this screen black-and-white, or in color/grayscale mode?
  184.     if ( 0 == TestDeviceAttribute( theMainGDev, gdDevType ) )
  185.     {
  186.         ShowStopAlert( "\pThis demo must run in color." );
  187.         goto Xit;
  188.     }
  189.     
  190.     //Could use HasDepth to determine depth of the monitor, but for our
  191.     //demos we're letting QuickDraw handle color translation. 
  192.  
  193.     returnMe = TRUE;
  194. Xit:
  195.     return returnMe;
  196. }//EnviroCheck
  197.  
  198.  
  199. //____ ShowStopAlert __________________________________________________________________________
  200. //
  201. //    This puts up a Stop Alert. The passed Pascal-style string is ParamText'ed into
  202. //    a small static-text area in the Alert and is appended with "Application halts."
  203. //
  204. void ShowStopAlert(StringPtr pascalMessage)
  205. {
  206. short    dummy;
  207.     ParamText(pascalMessage, "\p  Application halts.", "\p", "\p");
  208.     dummy = StopAlert(kOurStopAlertID,NULL);
  209. }//ShowStopAlert
  210.  
  211.  
  212.  
  213. //____ GetAppName __________________________________________________________________________
  214. //
  215. //    Uses the Process Manager to find out our App's Name
  216. //    (better than using CurAppName lo-mem global, which
  217. //    may break.) Call this once, during startup procedure.
  218. //
  219. void GetAppName(char *pascalName)
  220. {
  221. OSErr                    err;
  222. ProcessSerialNumber        PSN;
  223. ProcessInfoRec            info;
  224.  
  225.     //get serial number of our process
  226.     err = GetCurrentProcess(&PSN);
  227.     if (noErr != err)
  228.         goto Xit;
  229.     
  230.     //funkyness here is required for GetProcessInformation()
  231.     info.processInfoLength = sizeof( ProcessInfoRec );
  232.     info.processAppSpec = nil;
  233.     info.processName = ( StringPtr ) pascalName;
  234.     
  235.     //learn a bunch of interesting things about our process,
  236.     //including it's name
  237.     err = GetProcessInformation(&PSN,&info);
  238.     
  239. Xit:
  240.     if (noErr != err)
  241.     {
  242.         BlockMove("\8Untitled", pascalName, 9);
  243.     }
  244.     
  245.     return;
  246.  
  247. }//GetAppName
  248.  
  249.  
  250. //____ InitRandomNumbers __________________________________________________________________________
  251. //
  252. void InitRandomNumbers( void )
  253. {
  254.     GetDateTime((unsigned long *)&qd.randSeed);
  255. }//InitRandomNumbers
  256.  
  257. //____ RandomRange __________________________________________________________________________
  258. //
  259. //    Given an unsigned short, this returns a value between zero and range - 1.
  260. //
  261. unsigned short RandomRange( unsigned short range )
  262. {
  263. long        aLong;    //treat return value as 0-65536
  264.  
  265.     aLong = Random();
  266.     aLong += 32767;
  267.     return (aLong * range) / 65536;     // now 0 <= t < range
  268. }//RandomRange
  269.  
  270.  
  271. //____ GlobalToLocalRect __________________________________________________________________________
  272. //
  273. //    Changes passed rectangle from Global to Local coördinates.
  274. //
  275. void GlobalToLocalRect(Rect *r)
  276. {
  277.     GlobalToLocal(&mTopLeft(*r));
  278.     GlobalToLocal(&mBotRight(*r));
  279. }//GlobalToLocalRect
  280.  
  281.  
  282. //____ LocalToGlobalRect __________________________________________________________________________
  283. //
  284. //    Changes passed rectangle from Local to Global coördinates.
  285. //
  286. void LocalToGlobalRect(Rect *r)
  287. {
  288.     LocalToGlobal(&mTopLeft(*r));
  289.     LocalToGlobal(&mBotRight(*r));
  290. }//LocalToGlobalRect
  291.  
  292.  
  293. //____ CenterRectInRect __________________________________________________________________________
  294. //
  295. //    Centers rect1 in rect2
  296. //
  297. void CenterRectInRect(Rect *rect1, Rect *rect2)
  298. {
  299.     mAssert((NULL != rect1) && (NULL != rect2));
  300.  
  301.     OffsetRect    (rect1,
  302.                 ((rect2->right - rect2->left) - (rect1->right - rect1->left))/2 - rect1->left,
  303.                 ((rect2->bottom - rect2->top) - (rect1->bottom - rect1->top))/2 - rect1->top);
  304. }//CenterRectInRect
  305.  
  306.  
  307. //____ CenterWindowInRect __________________________________________________________________________
  308. //
  309. //    Serviceable centering code; will center dialogs, too.
  310. //    Note that we ignore menubar height.
  311. //
  312. //    Incidentally, windows look nicer if they're centered horizontally and
  313. //    1/3 of the way down from the top of the screen. Pass in TRUE for oneThird,
  314. //    and that's where the window will wind up; pass in FALSE and window will
  315. //    be centered square in the middle of boundRect.
  316. //
  317. //
  318. void CenterWindowInRect(WindowPtr wind, Rect *boundRect, Boolean oneThird)
  319. {
  320. Rect        windRect, bounds;
  321.  
  322.     mAssert(NULL != wind);
  323.     mAssert(NULL != boundRect);
  324.     
  325.     if ((NULL == wind) || (NULL == boundRect))
  326.         return;
  327.         
  328.     //It's better if we can get use the structure rect for centering,
  329.     //but we won't fuss around if we can't get it. (You could be
  330.     //clever and move the window way off somewhere, show it, grab the
  331.     //structure bounds, hide the window, and offset both objects back
  332.     //to where they ought to be.)
  333.     if (((WindowPeek)wind)->visible)
  334.         windRect = (*(((WindowPeek)wind)->strucRgn))->rgnBBox;
  335.     else
  336.         windRect = wind->portRect;
  337.  
  338.     bounds = *boundRect;
  339.     
  340.     mAssert(!EmptyRect(&bounds));
  341.     mAssert(!EmptyRect(&windRect));
  342.  
  343.     if (oneThird)
  344.     {
  345.         MoveWindow(wind,    ((bounds.right - bounds.left) - (windRect.right - windRect.left))/2 + bounds.left,
  346.                             ((bounds.bottom - bounds.top) - (windRect.bottom - windRect.top))/3 + bounds.top,
  347.                             TRUE);
  348.     }
  349.     else
  350.     {
  351.         CenterRectInRect(&windRect, &bounds);
  352.         MoveWindow(wind, windRect.left, windRect.top, TRUE);
  353.     }
  354.  
  355. }//CenterWindowInRect
  356.  
  357.  
  358. //____ GetMaxIntersectDevice __________________________________________________________________________
  359. //
  360. //    Given a rectangle in global coördinates, this will return the device
  361. //    on which the majority of the rectangle overlaps.
  362. //
  363. GDHandle GetMaxIntersectDevice(Rect globalRect)
  364. {
  365. long        area, maxArea;                //must be longs; shorts may overflow.
  366. GDHandle    device, deviceToReturn;
  367. Rect        intersection;
  368.  
  369.     deviceToReturn = GetMainDevice();
  370.     maxArea = 0;
  371.  
  372.     for (device = GetDeviceList(); device != NULL ; device = GetNextDevice(device))
  373.     {
  374.         //determine if device is a screen, if it's active, and our rect intersects it
  375.         if ((TestDeviceAttribute(device, screenDevice)
  376.           && TestDeviceAttribute(device, screenActive)
  377.           && SectRect(&globalRect, &((*device)->gdRect), &intersection)))
  378.           {
  379.             area = ((long)(intersection.right - intersection.left)) *
  380.                    ((long)(intersection.bottom - intersection.top));
  381.             if (area > maxArea)
  382.             {
  383.                 deviceToReturn = device;
  384.                 maxArea = area;
  385.             }
  386.         }
  387.     }
  388.     return(deviceToReturn);
  389. }//GetMaxIntersectDevice
  390.  
  391.  
  392. //____ GetLargestAreaDevice __________________________________________________________________________
  393. //
  394. //    Returns device with largest area.
  395. //
  396. GDHandle GetLargestAreaDevice( void )
  397. {
  398. long        area, maxArea;                //make sure these are longs!!
  399. GDHandle    device, deviceToReturn;
  400.  
  401.     deviceToReturn = GetMainDevice();
  402.     maxArea = 0;
  403.  
  404.     for (device = GetDeviceList(); device != NULL ; device = GetNextDevice(device))
  405.     {
  406.         //determine if device is a screen, and if it's active
  407.         if ((TestDeviceAttribute(device, screenDevice)
  408.           && TestDeviceAttribute(device, screenActive)))
  409.           {
  410.               area = ((long)(*device)->gdRect.bottom - (*device)->gdRect.top)
  411.                    * ((long)(*device)->gdRect.right - (*device)->gdRect.left);
  412.             if (area > maxArea)
  413.             {
  414.                 deviceToReturn = device;
  415.                 maxArea = area;
  416.             }
  417.         }
  418.     }
  419.  
  420.     return(deviceToReturn);
  421.  
  422. }//GetLargestAreaDevice
  423.  
  424.  
  425. //____ CountAvailableDevices __________________________________________________________________________
  426. //
  427. //    Walks thru public GDevice list and tells us how many active screens there are
  428. //
  429. short CountAvailableDevices( void )
  430. {
  431. short        count = 0;
  432. GDHandle    aDevice;
  433.  
  434.     for (aDevice = GetDeviceList(); aDevice != NULL ; aDevice = GetNextDevice(aDevice))
  435.     {
  436.         if (    (TestDeviceAttribute(aDevice, screenDevice))
  437.            &&    (TestDeviceAttribute(aDevice, screenActive)))
  438.              count += 1;
  439.     }
  440.     
  441.     mAssert(count > 0);
  442.     
  443.     return count;
  444.  
  445. }//CountAvailableDevices
  446.  
  447.  
  448.  
  449.  
  450. #pragma mark -
  451. //   string utils
  452.  
  453.  
  454.  
  455. //____ CLen __________________________________________________________________________
  456. //
  457. //    Returns length of C string; analogous to strlen.
  458. //
  459. long CLen(char *s)
  460. {
  461. char    *p;
  462. long    len;
  463.  
  464.     p = s;
  465.     while (*p++ != '\0')
  466.         ;//twiddle
  467.  
  468.     len = (--p - s);
  469.  
  470.     return(len);
  471. }//CLen
  472.  
  473.  
  474. //____ PLen __________________________________________________________________________
  475. //
  476. //    Returns length of Pascal string.
  477. //
  478. short PLen(StringPtr s)
  479. {
  480.     mAssert(*s >= 0);
  481.     return ((short)*s);    //Pascal strings store lenght in first byte
  482. }//PLen
  483.  
  484.  
  485. //____ Pas2c __________________________________________________________________________
  486. //
  487. //    Converts a string from Pascal to C in place
  488. //    
  489. char *Pas2c(unsigned char *str)
  490. {
  491. unsigned short        len;
  492.  
  493.     len = str[0];
  494.     mAssert ((len >= 0) || (len <= 255));
  495.     if ((len < 0) || (len > 255))    //insanity check -- do something reasonable
  496.         len = 255;
  497.     BlockMove(str + 1, str, len);    //BlockMove properly handles overlapping blocks
  498.     str[len] = '\0';
  499.  
  500.     return((char *)str);
  501. }//Pas2c
  502.  
  503.  
  504. //____ C2pas __________________________________________________________________________
  505. //
  506. //    Converts a C string into Pascal variety. Will truncate to 255 chars if needed.
  507. //
  508. unsigned char *C2pas(char *str)
  509. {
  510.  
  511. long    len;
  512.  
  513.     len = CLen(str);
  514.     
  515.     if ((len < 0) || (len > 255))
  516.         len = 255;
  517.         
  518.     BlockMove(str, str + 1, len);    //BlockMove properly handles overlapping blocks
  519.     str[0] = len;
  520.     return((unsigned char *)str);
  521.  
  522. }//C2pas
  523.  
  524.  
  525.  
  526. //____ CCpy __________________________________________________________________________
  527. //
  528. //    analogous to ccpy
  529. //
  530. char *CCpy(char *dest, char *src)
  531. {
  532. long        len;
  533.  
  534.     len = CLen(src);
  535.     
  536.     BlockMove(src, dest, len + 1);
  537.  
  538.     return(dest);
  539. }
  540.  
  541.  
  542.  
  543. //____ PCpy __________________________________________________________________________
  544. //
  545. //    Copies Pascal string from src to dest
  546. //
  547. unsigned char *PCpy(StringPtr dest, StringPtr src)
  548. {
  549. long        len;
  550.  
  551.     len = PLen(src);
  552.     
  553.     BlockMove(src, dest, len + 1);
  554.  
  555.     return(dest);
  556. }
  557.  
  558.  
  559. //____ CWrite __________________________________________________________________________
  560. //
  561. //    Draws Pascal string centered on x, with baseline at y
  562. //
  563. void CWrite( short x, short y, ConstStr255Param s )
  564. {
  565.     MoveTo(x - StringWidth(s) / 2, y);
  566.     DrawString(s);
  567. }
  568.  
  569.  
  570.  
  571. //____ ParamAString __________________________________________________________________________
  572. //
  573. //    This convenient routine allows you to param a single Pascal string.
  574. //
  575. void ParamAString( ConstStr255Param theStr )
  576. {
  577.     ParamText(theStr, "\p", "\p", "\p");
  578. } //ParamAString
  579.